using System;
using System.Collections.Generic;
using System.Text;
using OracleInternal.Json.Common;

namespace OracleInternal.Json.Binary
{
	internal class OsonHeader
	{
		private static byte[][] UTF8_MAPPING;

		private int version;

		private int flags;

		private int[] fieldNameOffsets;

		private int[] hashIdArray;

		internal string[] fieldNames;

		private int ubHash;

		private int ubFieldId;

		private int fieldHeapSize;

		private int treeSegmentSize;

		internal int treeSegmentOffset;

		private int tinyNodeCount;

		internal int updateFlags;

		internal int extendedTreeSegmentOffset;

		internal Dictionary<int, int> forwardingAddress;

		static OsonHeader()
		{
			UTF8_MAPPING = new byte[256][];
			char[] array = new char[1];
			for (int i = 0; i < UTF8_MAPPING.Length; i++)
			{
				array[0] = (char)i;
				UTF8_MAPPING[i] = Encoding.UTF8.GetBytes(array);
			}
		}

		internal OsonHeader(OsonBuffer b, IExceptionFactory f)
		{
			ReadHeader(b, f);
			if (IsScalar())
			{
				treeSegmentOffset = b.Position();
				return;
			}
			ReadHashIds(b, f);
			if (IsSet(2048))
			{
				Ub4int(b, fieldNameOffsets);
			}
			else
			{
				Uub2(b, fieldNameOffsets);
			}
			int num = b.Position();
			if (fieldNameOffsets.Length != 0)
			{
				treeSegmentOffset = -1;
				for (int i = 0; i < fieldNameOffsets.Length; i++)
				{
					b.Position(fieldNameOffsets[i] + num);
					int uB = b.GetUB1();
					int charArrLen;
					char[] value = b.ReadString(uB, out charArrLen);
					fieldNames[i] = new string(value, 0, uB);
					if (b.Position() > treeSegmentOffset)
					{
						treeSegmentOffset = b.Position();
					}
				}
			}
			else
			{
				treeSegmentOffset = b.Position();
			}
			if (!IsSet(4) || !IsSet(2) || IsSet(1))
			{
				throw new NotSupportedException();
			}
			int num2 = treeSegmentOffset + treeSegmentSize;
			if (num2 < b.Buffer().Limit())
			{
				b.Position(num2);
				ReadUpdateHeader(b, f);
			}
		}

		internal void ReadHeader(OsonBuffer b, IExceptionFactory f)
		{
			if (b.Buffer().Remaining() <= 6)
			{
				throw OracleJsonExceptions.CORRUPT2.Create(f, b.m_buffer.Remaining());
			}
			int @int = b.GetInt();
			if ((@int & 0xFFFFFF00u) != 4283062784u)
			{
				throw OracleJsonExceptions.CORRUPT.Create(f);
			}
			version = @int & 0xFF;
			if (version < 1 || version > 2)
			{
				throw OracleJsonExceptions.UNSUPPORTED_VERSION.Create(f, version);
			}
			flags = b.GetUB2();
			if (!IsSet(16))
			{
				int num;
				if (IsSet(8))
				{
					ubFieldId = 4;
					num = b.GetUB4int();
				}
				else if (IsSet(1024))
				{
					ubFieldId = 2;
					num = b.GetUB2();
				}
				else
				{
					ubFieldId = 1;
					num = b.GetUB1();
				}
				fieldNameOffsets = new int[num];
				hashIdArray = new int[num];
				fieldNames = new string[num];
				fieldHeapSize = (IsSet(2048) ? b.GetUB4int() : b.GetUB2());
				treeSegmentSize = (IsSet(4096) ? b.GetUB4int() : b.GetUB2());
				tinyNodeCount = b.GetUB2();
			}
			else
			{
				treeSegmentSize = (IsSet(4096) ? b.GetUB4int() : b.GetUB2());
			}
		}

		private void ReadUpdateHeader(OsonBuffer b, IExceptionFactory f)
		{
			updateFlags = b.GetUB2();
			int uB = b.GetUB2();
			b.GetUB4int();
			int uB4int = b.GetUB4int();
			b.GetUB4int();
			extendedTreeSegmentOffset = b.Position() + uB4int;
			forwardingAddress = new Dictionary<int, int>();
			if (IsSetUpd(256))
			{
				for (int i = 0; i < uB; i++)
				{
					forwardingAddress.Add(b.GetUB2(), b.GetUB2());
				}
			}
			else
			{
				for (int j = 0; j < uB; j++)
				{
					forwardingAddress.Add(b.GetUB4int(), b.GetUB4int());
				}
			}
		}

		internal bool IsScalar()
		{
			return IsSet(16);
		}

		internal bool IsTinyNodeCount()
		{
			return IsSet(8192);
		}

		internal void ReadHashIds(OsonBuffer b, IExceptionFactory f)
		{
			if (IsSet(256))
			{
				ubHash = 1;
				Ub1(b, hashIdArray);
				return;
			}
			if (hashIdArray.Length != 0)
			{
				throw OracleJsonExceptions.NOT_IMPLEMENTED.Create(f);
			}
			ubHash = 1;
		}

		private void Ub1(OsonBuffer s, int[] arr)
		{
			for (int i = 0; i < arr.Length; i++)
			{
				arr[i] = s.GetUB1();
			}
		}

		private void Uub2(OsonBuffer s, int[] arr)
		{
			for (int i = 0; i < arr.Length; i++)
			{
				arr[i] = s.GetUB2();
			}
		}

		private void Ub4int(OsonBuffer s, int[] arr)
		{
			for (int i = 0; i < arr.Length; i++)
			{
				arr[i] = s.GetUB4int();
			}
		}

		internal int GetTreeSegmentOffset()
		{
			return treeSegmentOffset;
		}

		internal int GetFieldHeapSize()
		{
			return fieldHeapSize;
		}

		internal string[] GetFields()
		{
			return fieldNames;
		}

		internal int[] HashIds()
		{
			return hashIdArray;
		}

		private bool IsSet(int f)
		{
			return (flags & f) != 0;
		}

		private bool IsSetUpd(int f)
		{
			return (updateFlags & f) != 0;
		}

		internal bool FieldsSorted()
		{
			return !IsSet(32768);
		}

		internal int GetFieldId(string key)
		{
			int num = Ohash(key);
			int num2 = Array.BinarySearch(hashIdArray, num);
			if (num2 < 0)
			{
				return -1;
			}
			while (num2 > 0 && hashIdArray[num2 - 1] == num)
			{
				num2--;
			}
			do
			{
				if (fieldNames[num2].Equals(key))
				{
					return num2 + 1;
				}
				num2++;
			}
			while (num2 < hashIdArray.Length && hashIdArray[num2] == num);
			return -1;
		}

		internal int Ohash(string key)
		{
			if (ubHash == 1)
			{
				return Ub1hash(key);
			}
			throw new IllegalStateException(OracleJsonExceptions.NOT_IMPLEMENTED.GetMessage());
		}

		internal static int Ub1hash(string key)
		{
			long num = 2166136261L;
			for (int i = 0; i < key.Length; i++)
			{
				char c = key[i];
				if (c < '\u0080')
				{
					num = (num ^ UTF8_MAPPING[c][0]) * 16777619;
					continue;
				}
				if (c < 'Ā')
				{
					num = (num ^ UTF8_MAPPING[c][0]) * 16777619;
					num = (num ^ UTF8_MAPPING[c][1]) * 16777619;
					continue;
				}
				int num2 = key[i];
				if (num2.ToString().Length == 2)
				{
					i++;
				}
				string s = num2.ToString();
				byte[] bytes = Encoding.UTF8.GetBytes(s);
				for (int j = 0; j < bytes.Length; j++)
				{
					num = (num ^ bytes[j]) * 16777619;
				}
			}
			long num3 = ((num << 24) & 0xFF000000u) | ((num << 8) & 0xFF0000) | ((num >> 8) & 0xFF00) | ((num >> 24) & 0xFF);
			return (int)((0xFF000000u & num3) >> 24);
		}

		internal int NumFieldIdBytes()
		{
			return ubFieldId;
		}
	}
}
